package com.gzhryc.common.secruity;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.security.*;

import javax.crypto.*;
import javax.crypto.spec.GCMParameterSpec;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import org.apache.commons.codec.binary.Base64;
import org.bouncycastle.jce.provider.BouncyCastleProvider;

import com.gzhryc.common.logging.Logger;

public class AESTools {
	
	static Logger log = Logger.getLogger(AESTools.class);

	private static AESTools aesTools;

	private AESTools() {
		log.info("添加解码包：" + BouncyCastleProvider.class.getName());
		Security.addProvider(new BouncyCastleProvider());
	}

	public static AESTools get() {
		if (aesTools == null) {
			aesTools = new AESTools();
		}
		return aesTools;
	}

	static final int KEY_LENGTH_BYTE = 32;
	static final int TAG_LENGTH_BIT = 128;
	/**
	 * 默认的字符编码
	 */
	private static final String DEFAULT_CHARSET = "utf-8";
	/**
	 * 解密AES 32位
	 *
	 * @param content   解密的内容
	 * @param openToken 秘钥
	 * @return 解密后的明文 数据
	 */
	public static String decrypt(String content, String openToken) {

		if (openToken == null) {
			log.error("需要的秘钥为空");
			return null;
		}
		try {
			byte[] bytes = java.util.Base64.getDecoder().decode(new StringBuilder(content).reverse().toString());//倒序转换并base64解码
			// 创建AES的Key生产者  这些与加密时保持一致
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			random.setSeed(openToken.getBytes());
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			// 创建AES的Key生产者
			kgen.init(128, random);
			// 根据用户密码，生成一个密钥
			SecretKey secretKey = kgen.generateKey();
			// 返回基本编码格式的密钥
			byte[] enCodeFormat = secretKey.getEncoded();
			// 转换为AES专用密钥
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			// 创建密码器
			Cipher cipher = Cipher.getInstance("AES");
			// 初始化为解密模式的密码器
			cipher.init(Cipher.DECRYPT_MODE, key);
			// 返回明文
			byte[] result = cipher.doFinal(bytes);
			return new String(result);
		} catch (Exception ex) {
			log.error("AES解密失败", ex);
			return null;
		}
	}


	/**
	 * 加密32位
	 *
	 * @param content 需要加密的内容
	 * @param sKey 秘钥
	 * @return 加密的内容
	 */
	public static String encrypt(String content, String sKey) {
		if (sKey == null) {
			log.error("需要的秘钥为空");
			return null;
		}
		try {
			SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
			random.setSeed(sKey.getBytes());
			// 创建AES的Key生产者
			KeyGenerator kgen = KeyGenerator.getInstance("AES");
			kgen.init(128, random);
			// 根据用户密码，生成一个密钥
			SecretKey secretKey = kgen.generateKey();
			// 返回基本编码格式的密钥，如果此密钥不支持编码，则返回
			byte[] enCodeFormat = secretKey.getEncoded();
			// 转换为AES专用密钥
			SecretKeySpec key = new SecretKeySpec(enCodeFormat, "AES");
			// 创建密码器
			Cipher cipher = Cipher.getInstance("AES");
			byte[] byteContent = content.getBytes(DEFAULT_CHARSET);
			// 初始化为加密模式的密码器
			cipher.init(Cipher.ENCRYPT_MODE, key);
			// 加密
			byte[] result = cipher.doFinal(byteContent);

			return new StringBuilder(java.util.Base64.getEncoder().encodeToString(result)).reverse().toString();
		} catch (Exception ex) {
			log.error("AES加密失败", ex);
			return null;
		}
	}

	/**
	 * AES解密
	 * @param content 密文
	 * @param keyByte
	 * @param ivByte
	 * @return
	 */
	public byte[] decrypt(byte[] content, byte[] keyByte, byte[] ivByte) {
		try {
			Cipher cipher = Cipher.getInstance("AES/CBC/PKCS7Padding");
			log.debug("加载：AES/CBC/PKCS7Padding");
			Key sKeySpec = new SecretKeySpec(keyByte, "AES");
			log.debug("组装：Key");
			AlgorithmParameters params = AlgorithmParameters.getInstance("AES");
			params.init(new IvParameterSpec(ivByte));
			log.debug("组装：AlgorithmParameters");
			cipher.init(Cipher.DECRYPT_MODE, sKeySpec, params);// 初始化
			byte[] result = cipher.doFinal(content);
			return result;
		} catch (NoSuchAlgorithmException e) {
			log.error(e.getMessage(),e);
		} catch (NoSuchPaddingException e) {
			log.error(e.getMessage(),e);
		} catch (InvalidKeyException e) {
			log.error(e.getMessage(),e);
		} catch (IllegalBlockSizeException e) {
			log.error(e.getMessage(),e);
		} catch (BadPaddingException e) {
			log.error(e.getMessage(),e);
		} catch (Exception e) {
			log.error(e.getMessage(),e);
		}
		return null;
	}
	
	public byte[] decryptBase64(String ciphertext, String key, String iv) {
		byte[] content = Base64.decodeBase64(ciphertext);
		byte[] keyByte = Base64.decodeBase64(key);
		byte[] ivByte = Base64.decodeBase64(iv);
		return decrypt(content,keyByte,ivByte);
	}
	
	public String decryptBase64(String ciphertext, String key, String iv,String charset) {
		byte[] content = Base64.decodeBase64(ciphertext);
		byte[] keyByte = Base64.decodeBase64(key);
		byte[] ivByte = Base64.decodeBase64(iv);
		try {
			return new String(decrypt(content,keyByte,ivByte),charset);
		} catch (UnsupportedEncodingException e) {
			e.printStackTrace();
		}
		return null;
	}

	/**
	 * AES解密
	 * @param content	密文
	 * @param keyByte
	 * @param associatedData
	 * @param nonce
	 * @return
	 * @throws GeneralSecurityException
	 * @throws IOException
	 */
	public byte[] decrypt(byte[] content, byte[] keyByte, byte[] associatedData, byte[] nonce) {
		try {
			Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");

			SecretKeySpec key = new SecretKeySpec(keyByte, "AES");
			GCMParameterSpec spec = new GCMParameterSpec(TAG_LENGTH_BIT, nonce);

			cipher.init(Cipher.DECRYPT_MODE, key, spec);
			cipher.updateAAD(associatedData);

			return cipher.doFinal(content);
		} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
			throw new IllegalStateException(e);
		} catch (InvalidKeyException | InvalidAlgorithmParameterException e) {
			throw new IllegalArgumentException(e);
		} catch (IllegalBlockSizeException e) {
			e.printStackTrace();
		} catch (BadPaddingException e) {
			e.printStackTrace();
		}
		return null;
	}
}
